package com.xian.im.web.common;

import com.xian.im.common.annotation.AnonymousAccess;
import com.xian.im.common.exception.BusinessException;
import com.xian.im.common.exception.GlobalErrorCode;
import com.xian.im.common.utils.ResponseUtil;
import com.xian.im.common.utils.sys.ThreadLocalUtil;
import com.xian.im.modules.security.BaseClaims;
import com.xian.im.modules.security.TokenProvider;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @program: xian-code
 * @description: token权限验证，未整合框架、简单实现
 * @author: liru.xian
 * @create: 2021-01-20 01:58
 **/
@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Autowired
    private TokenProvider tokenProvider;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private Environment environment;

    public static final String TOKEN = "eyJhbGciOiJIUzUxMiJ9.eyJ0eSI6MSwidCI6MSwiYWsiOiJhZDM2ZWE1MjM4NTA0MjE2YWUzY2E0NzE3ODBiMTIyNSIsInNrIjoiN2U4MWFhOWIzYzEyZTBiZWI0NTA3OGQwNWZhOGFhYWEifQ.jvHs8i0YhLeLt729EWV3nBxbszNTJF7VQcZC7IGHeR8nLsJTmTY9JUr321ipB6bt14WvHSuhrg8ZxRbfQeqUuw";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求头（如果有此请求头，表示token已经签发）
        if(request.getRequestURI().endsWith("error")){
            return Boolean.TRUE;
        }
        if (isMatch(handler) || isMatch(request.getRequestURI(), WebMvcConfig.stringList)) {
            return Boolean.TRUE;
        }

        String active = environment.getProperty("spring.profiles.active");
        String token = request.getHeader(TokenProvider.TOKEN_HEAD_NAME);

        if (StringUtils.isNotBlank(token)) {
            //解析请求头（防止伪造token，token内容以"token "作为开头）
            try {
                BaseClaims claims = tokenProvider.resolveBaseClaims(token);
                String redisKey = claims.getRedisKey();
                ThreadLocalUtil.set(TokenProvider.USER_CLAIMS,claims);
                Object o = redisTemplate.opsForValue().get(redisKey);
                if (o == null) {
                    if (TOKEN.equals(token) && !"pro".equals(active)) {
                        return true;
                    }
                    return false;
                }
            } catch (BusinessException e) {
                ResponseUtil.renderJson(response, GlobalErrorCode.UNAUTHORIZED.getMessage(), e.getStatus());
                return false;
            }
        } else {
            ResponseUtil.renderJson(response, GlobalErrorCode.UNAUTHORIZED.getMessage(), 401);
            return false;
        }
        //所有请求都通过，具体权限在service层判断
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        ThreadLocalUtil.remove();
    }


        private Boolean isMatch(String url, List<String> urlList) {
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String s : urlList) {
            if (pathMatcher.match(s, url)) {
                return true;
            }
        }
        return Boolean.FALSE;
    }

    private Boolean isMatch(Object handler) {
        if(handler instanceof HandlerMethod){
            HandlerMethod h = (HandlerMethod) handler;
            AnonymousAccess classAnnotation = h.getBean().getClass().getAnnotation(AnonymousAccess.class);
            if (classAnnotation != null) {
                return Boolean.TRUE;
            }
            //获取接口上的reqeustmapping注解
            AnonymousAccess methodAnnotation = h.getMethodAnnotation(AnonymousAccess.class);
            if (methodAnnotation != null) {
                return Boolean.TRUE;
            }
        }
        if(handler instanceof ResourceHttpRequestHandler){
           return true;
        }

        return Boolean.FALSE;
    }
}
